package cn.com.doone.common.uc.oauth.authorize;

import static cn.com.doone.common.uc.domain.oauth.Constants.OAUTH_APPROVAL_VIEW;
import static cn.com.doone.common.uc.domain.oauth.Constants.OAUTH_LOGIN_VIEW;
import static cn.com.doone.common.uc.domain.oauth.Constants.REQUEST_USERNAME;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.http.NameValuePair;
import org.apache.oltu.oauth2.as.response.OAuthASResponse;
import org.apache.oltu.oauth2.common.error.OAuthError;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.OAuthResponse;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;

import cn.com.doone.common.uc.domain.oauth.AccessToken;
import cn.com.doone.common.uc.oauth.OAuthAuthxRequest;
import cn.com.doone.common.uc.oauth.shiro.OAuth2Token;
import cn.com.doone.common.uc.oauth.validator.AbstractClientDetailsValidator;
import cn.com.doone.common.uc.oauth.validator.WeChatClientDetailValidator;
import cn.com.doone.common.uc.utils.HttpClientUtil;
import cn.com.doone.common.uc.utils.PropertiesUtils;
import cn.com.doone.common.uc.utils.StringUtils;
import cn.com.doone.common.uc.web.WebUtils;

public class WeChatAuthorizeHandler extends AbstractAuthorizeHandler {
	
	private static final Logger LOG = LoggerFactory.getLogger(WeChatAuthorizeHandler.class);
	
	private AccessToken accessToken;

    @SuppressWarnings("unused")
	private PropertiesUtils propertiesUtils;

	public WeChatAuthorizeHandler(OAuthAuthxRequest oauthRequest, HttpServletResponse response,PropertiesUtils propertiesUtils) {
		super(oauthRequest, response);
		this.propertiesUtils = propertiesUtils;
	}

	@Override
	protected AbstractClientDetailsValidator getValidator() {
		return new WeChatClientDetailValidator(oauthRequest, oauthRequest.request());
	}
	
	public void handle() throws Exception {
		
		LOG.debug("######################   微信登录START   ###########################");
		
		SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		
		HttpServletRequest request = oauthRequest.request();
		String isBinding = (String) request.getParameter("is_binding");
		if ("true".equals(isBinding)) {
			if (!oauthService.bindAuthAccount(request, "WECHAT")) {
				request.setAttribute("auth_account", request.getParameter("auth_account"));
				request.setAttribute("username", request.getAttribute(REQUEST_USERNAME));
        		request.setAttribute("thd_app_name", "微信");
        		request.setAttribute("interface_url", "/oauth/wechatRedirectAuth");
        		request.setAttribute("app_icon_url", request.getParameter("app_icon_url"));
	            request.setAttribute("oauth_login_error", true);
	            request.setAttribute("oauth_login_error_message", "用户名或密码错误！！");
	            request.getRequestDispatcher("oauth_binding").forward(request, response);
				return;
			}
		}
		
		LOG.debug("WECHAT身份认证开始时间：" + df.format(new Date()));
		
		if (WeChatValid()) {
			return;
		}
    	
		LOG.debug("WECHAT身份认证结束时间：" + df.format(new Date()));
		
    	//valiblackip
    	/*if(valiBlackIP()) {
    		return;
    	}*/
    	
		LOG.debug("请求合法性认证开始时间：" + df.format(new Date()));
		
        // validate
        if (validateFailed()) {
            return;
        }
        
        LOG.debug("请求合法性认证结束时间：" + df.format(new Date()));

        LOG.debug("是否需要登录认证开始时间：" + df.format(new Date()));
        // Check need usr login
        /*if (goLogin()) {
            return;
        }*/
        
        LOG.debug("是否需要登录认证开始时间：" + df.format(new Date()));

        // 认证IP锁
        /*if (validIpLock()){
        	return;
        }*/
        
        // 是否需要登陆 
        /*if (isSubmitLogin()) {
        	// 账号锁
            if (validAccountLock()) {
            	return;
            }
        }*/
        
        LOG.debug("登录请求认证开始时间：" + df.format(new Date()));
        
        //submit login
        if (submitLogin()) {
            return;
        }
        
        LOG.debug("登录请求认证结束时间：" + df.format(new Date()));
    	// 验证是否为弱密码
        /*if (isWeakPassword()) {
        	return;
        }*/
        
        LOG.debug("获取账号及应用信息开始时间：" + df.format(new Date()));
        
        // 获取并设置userId和appInfoId
        this.setAttribute();
        
        LOG.debug("获取账号及应用信息结束时间：" + df.format(new Date()));
        
        
        LOG.debug("用户组权限认证开始时间：" + df.format(new Date()));
        // vali authorize
        if(valiAuthorize()) {
        	return;
        }
        LOG.debug("用户组权限认证结束时间：" + df.format(new Date()));

        LOG.debug("用户自身授权认证开始时间：" + df.format(new Date()));
        // Check approval
        if (goApproval()) {
            return;
        }

        // Submit approval
        if (submitApproval()) {
            return;
        }
        LOG.debug("用户自身授权认证结束时间：" + df.format(new Date()));

        //handle response
        handleResponse();
    }

	private boolean WeChatValid() throws Exception {
		
		HttpServletRequest request = oauthRequest.request();
		
		// 根据微信APPID获取应用信息
		String appid = request.getParameter("appid");
		String clientId = request.getParameter("client_id");
		Map<String, Object> appInfoMap = oauthService.findAppInfoByWeChatId(clientId, appid);
		
		if (appInfoMap == null) {
			final OAuthResponse oAuthResponse = OAuthASResponse
    				.authorizationResponse(oauthRequest.request(), HttpServletResponse.SC_FOUND)
                    .location(request.getContextPath() + "/resources/wechat_auth_error.html")
                    .buildQueryMessage();
    		
            WebUtils.writeOAuthQueryResponse(response, oAuthResponse);
			return true;
		}
		
		String isBinding = (String) request.getParameter("is_binding");
		
		if ("true".equals(isBinding)) {
			request.setAttribute("client_secret", (String) appInfoMap.get("APP_SECRET"));
        	request.setAttribute("redirect_uri", (String) request.getParameter("redirect_uri"));
        	request.setAttribute(REQUEST_USERNAME, (String) request.getParameter("username"));
		} else {
			String code = request.getParameter("code");
			
			List<NameValuePair> formParams = new ArrayList<NameValuePair>();
	        
	        Map<String, Object> tokenMap = HttpClientUtil.doPost("https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appid + "&secret=" + (String) appInfoMap.get("WECHAT_APP_SECRET") + "&code=" + code + "&grant_type=authorization_code", formParams);
	        
	        
	        if (tokenMap.get("errcode") == null) {
	        	
	        	// String accessToken = (String) tokenMap.get("access_token");
	            String openid = (String) tokenMap.get("openid");
	    		
	            // 根据微信openid获取用户中心账号
	    		Map<String, Object> ucUserMap = oauthService.findUserInfoByAppAccount("WECHAT", openid);
	    		
	    		request.setAttribute("client_secret", (String) appInfoMap.get("APP_SECRET"));
	        	request.setAttribute("redirect_uri", (String) request.getParameter("redirect_uri"));
	        	
	        	if (ucUserMap != null) {
	        		request.setAttribute(REQUEST_USERNAME, ucUserMap.get("USER_ACCOUNT"));
	        	} else {
	        		request.setAttribute("auth_account", openid);
	        		request.setAttribute("thd_app_name", "微信");
	        		request.setAttribute("interface_url", "/oauth/wechatRedirectAuth");
	        		request.setAttribute("app_icon_url", propertiesUtils.getHeadPath() + appInfoMap.get("APP_ICON_URI"));
	                request.getRequestDispatcher("oauth_binding").forward(request, response);
	                return true;
	        	}
	    	} else {
	    		
	    		final OAuthResponse oAuthResponse = OAuthASResponse
	    				.authorizationResponse(oauthRequest.request(), HttpServletResponse.SC_FOUND)
	                    .location(request.getContextPath() + "/resources/wechat_error.html")
	                    .buildQueryMessage();
	    		
	            WebUtils.writeOAuthQueryResponse(response, oAuthResponse);
	    		
	    		return true;
	    	}
		}
		
    	accessToken = oauthService.loadAccessToken("XDW_UC_MANAGER", (String) request.getAttribute(REQUEST_USERNAME), null);

    	if (accessToken != null) {
    		if (accessToken.tokenExpired()) {	// AccessToken已过期
    			if (accessToken.refreshTokenExpired()) {	// 刷新令牌已过期
    				accessToken = oauthService.retrieveNewAccessTokenByUsername(clientDetails(), (String) request.getAttribute(REQUEST_USERNAME));
    			} else {									// 刷新令牌已过期
    				accessToken = oauthService.changeAccessTokenByRefreshToken(accessToken.refreshToken(), accessToken.clientId());
    			}
    		}
    	} else {
    		accessToken = oauthService.retrieveNewAccessTokenByUsername(clientDetails(), (String) request.getAttribute(REQUEST_USERNAME));
    	}
        
		return false;
	}
	
	
	@Override
	protected boolean submitLogin() throws  ServletException, IOException {
		if (isSubmitLogin()) {
            //login flow
            try {
            	final HttpServletRequest request = oauthRequest.request();
            	
            	System.out.println("------------------------->" + request.getAttribute(REQUEST_USERNAME));
            	
            	AuthenticationToken token = new OAuth2Token(accessToken.tokenId(), "os-resource").setUserId((String) request.getAttribute(REQUEST_USERNAME));
            	
                SecurityUtils.getSubject().login(token);
                
                // 保存登录日志
                this.logLogin((String) request.getAttribute(REQUEST_USERNAME), "1");
                
                // 更新账号锁定状态
                Map<String,Object> userMap = new HashMap<String,Object>();
                int userInfoId = this.oauthService.findUserInfoIdByAccount((String) request.getAttribute(REQUEST_USERNAME));
    			userMap.put("userInfoId", userInfoId);
    			userMap.put("lockLevel", "0");
    			userMap.put("invalidTime", null);
    			
    			this.oauthService.updateUserLock(userMap);

                LOG.debug("Submit login successful");
                
                this.userFirstLogged = true;
                return false;
            } catch (Exception ex) {
            	
            	ex.printStackTrace();
                //login failed
                LOG.debug("Login failed, back to login page too", ex);
                final HttpServletRequest request = oauthRequest.request();
                
                // 保存登录日志
                this.logLogin((String) request.getAttribute(REQUEST_USERNAME), "0");

                request.setAttribute("username", request.getAttribute(REQUEST_USERNAME));
                request.setAttribute("oauth_login_error", true);
                request.setAttribute("oauth_login_error_message", "用户名或密码错误！！");
                request.getRequestDispatcher(OAUTH_LOGIN_VIEW).forward(request, response);
                return true;
            }
        }
        return false;
	}
	
	protected void responseApprovalDeny() throws IOException, OAuthSystemException {

		String redirectUri = (String) oauthRequest.request().getAttribute("redirect_uri");
		// redirectUri = redirectUri.replaceAll("%26", "&");
		
        final OAuthResponse oAuthResponse = OAuthASResponse.errorResponse(HttpServletResponse.SC_FOUND)
                .setError(OAuthError.CodeResponse.ACCESS_DENIED)
                .setErrorDescription("User denied access")
                // .location(clientDetails().getRedirectUri())
                .location(redirectUri)
                .setState(oauthRequest.getState())
                .buildQueryMessage();
        LOG.debug("'ACCESS_DENIED' response: {}");

        WebUtils.writeOAuthQueryResponse(response, oAuthResponse);

        //user logout when deny
        final Subject subject = SecurityUtils.getSubject();
        subject.logout();
        LOG.debug("After 'ACCESS_DENIED' call logout. user: {}");
    }
	
	protected boolean goLogin() throws ServletException, IOException {
        if (isNeedUserLogin()) {
            //go to login
            LOG.debug("Forward to Oauth login by client_id '{}'");
            final HttpServletRequest request = oauthRequest.request();
            request.getRequestDispatcher(OAUTH_LOGIN_VIEW)
                    .forward(request, response);
            return true;
        }
        return false;
    }
	
	protected boolean isNeedUserLogin() {
        return !isUserAuthenticated();
    }
	
	protected boolean goApproval() throws ServletException, IOException {
    	// 验证用户是否已授权访问应用
		if(this.oauthService.valiUserAndAppAuthorize(this.getUserId(), this.getAppId())) {	// 已授权
			userFirstLogged = false;
		}
        if (userFirstLogged && !clientDetails().trusted()) {
            //go to approval
            LOG.debug("Go to oauth_approval, clientId: '{}'");
            final HttpServletRequest request = oauthRequest.request();
            request.getRequestDispatcher(OAUTH_APPROVAL_VIEW).forward(request, response);
            return true;
        }
        return false;
    }
	

	@Override
	protected void handleResponse() throws OAuthSystemException, IOException {
		
		String redirectUri = (String) oauthRequest.request().getAttribute("redirect_uri");

//		Map<String, Object> appInfo = this.oauthService.findAppInfo(clientId());
//		Map<String,Object> logMap = new HashMap<String,Object>();
//		logMap.put("userAccount", this.getUserInfo().get("USER_ACCOUNT"));
//		logMap.put("logType", "WEIXIN");
//		logMap.put("app", StringUtils.isNull(this.getAppInfo().get("APP_CODE"))?this.getAppInfo().get("APP_NAME"):this.getAppInfo().get("APP_CODE"));
//		logMap.put("success", true);
//		LOG.info(JSON.toJSONString(logMap));
		
		final OAuthResponse oAuthResponse = OAuthASResponse.authorizationResponse(oauthRequest.request(), HttpServletResponse.SC_FOUND)
                // .location(clientDetails.getRedirectUri())
                .location(redirectUri)
                .buildQueryMessage();
        
		
        LOG.debug(" 'code' response: {}", oAuthResponse);
        
        LOG.debug("######################   微信登录END   ###########################");
        WebUtils.writeOAuthQueryResponse(response, oAuthResponse);
	}

}
